/*!
* @file Matrix4D.h
* @brief 4D矩阵
* @date 2024/9/12
*/
#pragma once

#include "DataDef.h"
#include "Vector3D.h"

namespace data
{
/*!
* @brief 4D矩阵
*/
class DATA_API Matrix4d
{
public:
    using traits_type = std::numeric_limits<double>;

    /*!
    * @brief 矩阵构造
    */
    Matrix4d();
    Matrix4d(double a11, double a12, double a13, double a14,
             double a21, double a22, double a23, double a24,
             double a31, double a32, double a33, double a34,
             double a41, double a42, double a43, double a44);
    Matrix4d(const Vector3d& r1, const Vector3d& r2, Vector3d& r3, const Vector3d& t);
    Matrix4d(const Matrix4d& mat);
    ~Matrix4d() = default;

    /*!
    * @brief 矩阵运算
    */
    Matrix4d& operator=(const Matrix4d& mat);
    Matrix4d operator+(const Matrix4d& mat) const;
    Matrix4d& operator+=(const Matrix4d& mat);
    Matrix4d operator-(const Matrix4d& mat) const;
    Matrix4d& operator-=(const Matrix4d& mat);
    Matrix4d operator*(const Matrix4d& mat) const;
    Matrix4d& operator*=(const Matrix4d& mat);
    Matrix4d operator*(double scalar) const;
    Matrix4d& operator*=(double scalar);

    bool operator==(const Matrix4d& mat) const;
    bool operator!=(const Matrix4d& mat) const;
    double* operator[](unsigned short index);
    const double* operator[](unsigned short index) const;

    double Det3(int r1, int r2, int r3, int c1, int c2, int c3) const;
    double Det3() const;
    double Det4() const;
    Matrix4d& Invert();
    Matrix4d Inverse() const;
    Matrix4d& Transpose();
    Matrix4d Transposed() const;

    Vector3d MultVec(const Vector3d& vec) const;
    Vector3d MultDir(const Vector3d& vec) const;

    /*!
    * @brief 矩阵操作
    */
    void GetMatrix(double matrix[16]) const;
    void SetMatrix(const double matrix[16]);
    const double& Value(unsigned short row, unsigned short col) const;
    void SetValue(unsigned short row, unsigned short col, const double& val);
    Vector3d Row(unsigned short index) const;
    void SetRow(unsigned short index, const Vector3d& vec);
    Vector3d Col(unsigned short index) const;
    void SetCol(unsigned short index, const Vector3d& vec);
    Vector3d Diagonal() const;
    void SetDiagonal(const Vector3d& vec);

    void MakeZero();
    void MakeIdentity();
    bool IsIdentity(double tol = 0.0) const;
    bool IsEqual(const Matrix4d& mat, double tol) const;

    void Move(double x, double y, double z);
    void Move(const Vector3d& vec);
    void Scale(double x, double y, double z);
    void Scale(const Vector3d& vec);
    void Scale(double scalexyz);
    void Rotate(const Vector3d& dir, double angle);

    void SetTranslate(const Vector3d& vec);
    void SetScale(const Vector3d& pos, double scale);
    void SetScale(const Vector3d& pos, const Vector3d& scales);
    void SetRotate(const Vector3d& pos, const Vector3d& dir, double angle);
    void SetMirror(const Vector3d& pos, const Vector3d& normal);
    bool IsMirror()const;

private:
    double _matrix[4][4];
};

/*!
* @brief 文本信息输出
*/
inline std::ostream& operator<< (std::ostream& stream, const Matrix4d& m)
{
    return stream << "{ "
        << m.Value(0, 0) << ", " << m.Value(0, 1) << ", " << m.Value(0, 2) << ", " << m.Value(0, 3) << ", "
        << m.Value(1, 0) << ", " << m.Value(1, 1) << ", " << m.Value(1, 2) << ", " << m.Value(1, 3) << ", "
        << m.Value(2, 0) << ", " << m.Value(2, 1) << ", " << m.Value(2, 2) << ", " << m.Value(2, 3) << ", "
        << m.Value(3, 0) << ", " << m.Value(3, 1) << ", " << m.Value(3, 2) << ", " << m.Value(3, 3) << " }";
}
}  // namespace data